package admin

import (
	"archive/zip"
	"exercise/application/yiran/app"
	"exercise/application/yiran/app/libs"
	"exercise/application/yiran/app/model"
	"exercise/application/yiran/app/request/adminRequest"
	"exercise/application/yiran/app/service"
	"fmt"
	"github.com/gin-gonic/gin"
	"io"
	"io/fs"
	"os"
	"path/filepath"
	"runtime"
	"strings"
)

type attachmentService struct {
}

// UncompletedTasks 校验是否存在未完成的任务
func (a *attachmentService) UncompletedTasks(ctx *gin.Context) error {
	schoolId, _ := ctx.Get("schoolId")
	src := filepath.ToSlash(filepath.Join(app.YiRanPublicDir, "notice", libs.Int64String(schoolId.(int64))))
	//1.校验文件是否存在
	b := libs.FileIsNotExist(src)
	if b {
		return fmt.Errorf("目标文件不存在")
	}

	//2.校验步长文件是否存在
	noticeFile := filepath.ToSlash(filepath.Join(app.YiRanPublicDir, "notice", libs.Int64String(schoolId.(int64))) + "_notice.json")
	b = libs.FileIsNotExist(noticeFile)
	if b {
		return fmt.Errorf("文件不存在")
	}

	return nil
}

// CheckNotice 校验文件夹数量
func (a *attachmentService) CheckNotice(request *adminRequest.CheckNoticeRequest) (ret interface{}, err error) {
	fileSrc := new(model.FileCenter)
	err = model.NewDb("").FindOne(fileSrc, "id = ?", []interface{}{request.Id}, "", nil)
	if err != nil {
		return nil, err
	}
	//1.校验文件是否存在
	filename := filepath.ToSlash(filepath.Join(app.YiRanPublicDir, fileSrc.SavePath))
	b := libs.FileIsNotExist(filename)
	if b {
		/*return nil, errors.New("文件不存在或已删除")*/
		return nil, fmt.Errorf("文件不存在或已删除")
	}

	//2.获取文件总数量
	zip, err := zip.OpenReader(filename)
	if err != nil {
		return nil, err
	}
	return map[string]interface{}{
		"total": len(zip.File),
	}, err
}

// UnZipNotice 解压
func (a *attachmentService) UnZipNotice(ctx *gin.Context, request *adminRequest.UnZipNoticeRequest) error {
	schoolId, _ := ctx.Get("schoolId")
	fileSrc := new(model.FileCenter)
	err := model.NewDb("").FindOne(fileSrc, "id = ?", []interface{}{request.Id}, "", nil)
	if err != nil {
		return err
	}
	//1.校验文件是否存在
	filename := filepath.ToSlash(filepath.Join(app.YiRanPublicDir, fileSrc.SavePath))
	b := libs.FileIsNotExist(filename)
	if b {
		return fmt.Errorf("文件不存在或已删除")
	}

	//2.解压
	destSrc := filepath.ToSlash(filepath.Join(app.YiRanPublicDir, "notice"))
	err = libs.UnzipFile(filename, filepath.Join(destSrc, libs.Int64String(schoolId.(int64))), true)
	if err != nil {
		return err
	}
	//3.删除源文件
	/*os.RemoveAll(filename)*/

	return nil
}

// SaveNotice 保存
func (a *attachmentService) SaveNotice(ctx *gin.Context, request *adminRequest.SaveNoticeRequest) (ret interface{}, err error) {
	schoolId, _ := ctx.Get("schoolId")
	isEnd := 1 //默认非最后一页
	src := filepath.ToSlash(filepath.Join(app.YiRanPublicDir, "notice", libs.Int64String(schoolId.(int64))))
	//1.校验文件是否存在
	b := libs.FileIsNotExist(src)
	if b {
		return nil, fmt.Errorf("目标文件不存在")
	}
	//2.获取文件及里的数据
	fileList, total, err := libs.AllFilesInDir(src)
	if err != nil {
		return nil, err
	}
	//3.获取开始位置
	noticeFile := filepath.ToSlash(filepath.Join(app.YiRanPublicDir, "notice", libs.Int64String(schoolId.(int64))) + "_notice.json")
	batch := 1000
	startIndex := a.getFileStartPage(noticeFile, total, batch)

	/*if total-startIndex < batch {
		batch = total - startIndex
		//1.删除页码文件
		os.RemoveAll(noticeFile)
		//2.最后一页标识
		isEnd = 2
		//3.删除文件夹,防止二次写入
		//os.RemoveAll(filepath.ToSlash(filepath.Join(app.YiRanPublicDir, "notice", libs.Int64String(schoolId.(int64)))))
	}
	endIndex := startIndex + batch*/
	//原本的最后一次,如果最后一次大于总数,表示最后一的最大值就是总数
	endIndex := startIndex + batch
	if endIndex > total {
		endIndex = total
		//1.删除页码文件
		os.RemoveAll(noticeFile)
		//2.最后一页标识
		isEnd = 2
		//3.删除文件夹,防止二次写入
		//os.RemoveAll(filepath.ToSlash(filepath.Join(app.YiRanPublicDir, "notice", libs.Int64String(schoolId.(int64)))))
	}

	list := fileList[startIndex:endIndex]
	return map[string]interface{}{
		"isEnd": isEnd,
		"total": total,
		"list":  list,
	}, nil
}

var zipStateManger = &service.ZipStateManager{}

// ZipNotice 轮询压缩zip包
func (a *attachmentService) ZipNotice(ctx *gin.Context) error {
	schoolId, _ := ctx.Get("schoolId")
	userId, _ := ctx.Get("userId")
	src := filepath.ToSlash(filepath.Join(app.YiRanPublicDir, "notice", libs.Int64String(schoolId.(int64))))

	fmt.Printf("src = %v \n", src)

	//1.校验文件是否存在
	b := libs.FileIsNotExist(src)
	if b {
		return fmt.Errorf("目标文件不存在")
	}
	fileList, total, err := libs.AllFilesInDir(src)
	if err != nil {
		return fmt.Errorf("文件数据异常：%w", err)
	}
	dest := filepath.ToSlash(filepath.Join(app.YiRanPublicDir, "notice"))
	startFile := filepath.ToSlash(filepath.Join(dest, libs.Int64String(schoolId.(int64))+"_"+libs.Int64String(userId.(int64))+"_start.json"))
	step := 2000
	startIndex := a.getFileStartPage(startFile, total, step)
	endIndex := startIndex + step

	if endIndex > total {
		endIndex = total
	}

	//压缩成功后的文件名
	zipSrc := filepath.ToSlash(filepath.Join(app.YiRanPublicDir, "zip", libs.Int64String(userId.(int64))+".zip"))

	if zipStateManger.ZipWriter == nil {
		file, err := os.OpenFile(zipSrc, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0755)
		if err != nil {
			return fmt.Errorf("打开或创建zip异常: %w", err)
		}
		zipStateManger.ZipWriter = zip.NewWriter(file)
		zipStateManger.ZipFilePath = src
		zipStateManger.File = file
	}

	for _, filePath := range fileList[startIndex:endIndex] {
		err = a.addFileToZip(zipStateManger.ZipWriter, filepath.Join(dest, libs.Int64String(schoolId.(int64))), filePath)
		if err != nil {
			continue
		}
	}

	//最后一次轮询关闭
	if endIndex == total {
		defer zipStateManger.File.Close()
		if err = zipStateManger.ZipWriter.Close(); err != nil {
			fmt.Printf("Failed to close the ZIP writer: %v", err)
		}
		zipStateManger.ZipWriter = nil
		os.Remove(startFile)
	}
	return nil
}

// getFileStartPage 获取文件步长
func (a *attachmentService) getFileStartPage(noticeFile string, total, step int) int {
	b := libs.FileIsNotExist(noticeFile)
	item := make(map[string]int, 0)
	//1.存在,读取里面的数据
	if !b {
		content, _ := libs.ReadFile(noticeFile)
		_ = libs.JsonDecode(content, &item)
		item["start"] += step
		libs.WritesAnyToFile(noticeFile, item)
		return item["start"]
	}
	//2.不存在,写入json文件里
	item["start"] = 0
	item["total"] = total
	libs.WritesAnyToFile(noticeFile, item)
	return item["start"]
}

func (a *attachmentService) addFileToZip(zipWriter *zip.Writer, attachmentDir, filePath string) error {
	// 获取文件信息
	info, err := os.Stat(filePath)
	if err != nil {
		return fmt.Errorf("获取文件信息异常：%w", err)
	}

	f, err := os.Open(filePath)
	if err != nil {
		return fmt.Errorf("文件不存在：%w", err)
	}
	defer f.Close()

	// 计算文件相对于附件临时目录的完整相对路径,最终的目的是只保留文件名
	relativePath, err := filepath.Rel(attachmentDir, filePath)
	if err != nil {
		return fmt.Errorf("获取文件相对路径异常：%w", err)
	}

	// 创建zip条目，并保留原始目录结构
	header, err := zip.FileInfoHeader(info)
	if err != nil {
		return fmt.Errorf("创建文件头信息失败：%w", err)
	}

	header.Name = filepath.ToSlash(relativePath) // 使用完整的相对路径，包含所有子目录

	// 创建zip writer
	writer, err := zipWriter.CreateHeader(header)
	if err != nil {
		return fmt.Errorf("创建zip文件头部失败：%w", err)
	}

	// 将文件内容写入zip writer
	_, err = io.Copy(writer, f)
	if err != nil {
		return fmt.Errorf("复制数据到zip文件时发生错误：%w", err)
	}

	return nil
}

func (a attachmentService) CreateNotice(ctx *gin.Context) ([]string, error) {
	libs.LoggerError(map[string]interface{}{
		"username": "zhangsan",
	})

	userId, _ := ctx.Get("userId")
	schoolId, _ := ctx.Get("schoolId")
	src := filepath.ToSlash(filepath.Join(app.YiRanPublicDir, "notice", libs.Int64String(schoolId.(int64))))
	list := make([]string, 0)
	err := filepath.Walk(src, func(path string, info fs.FileInfo, err error) error {
		if !info.IsDir() {
			list = append(list, info.Name())
		}
		return err
	})

	//1.将所有的数据全部放到通道中
	listChan := make(chan string, len(list))
	go func(c chan string, list []string) {
		defer close(c)
		for _, v := range list {
			c <- v
		}
	}(listChan, list)

	for i := 0; i < runtime.NumCPU()*2; i++ {
		go func(list chan string) {
			for v := range list {
				vTmp := strings.Split(v, ".")
				notice := new(model.Notice)
				notice.UserId = userId.(int64)
				notice.SchoolId = schoolId.(int64)
				notice.IdCard = vTmp[0]
				notice.Avatar = filepath.ToSlash(filepath.Join(src, v))
				notice.CreateTime = libs.CurrentTime()
				model.NewDb("").InsertRecord(notice)
			}
		}(listChan)
	}
	return list, err
}

func NewAttachmentService() *attachmentService {
	return &attachmentService{}
}
